package com.horizon.common.configuration;

import com.horizon.common.util.DateUtil;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Set;

/**
 * 数据转换配置
 *
 * @author wangfuwei
 * @version 2018-08-06
 */
@SpringBootConfiguration
public class DataConverterConfiguration {

    /**
     * 自定义日期转换
     * <p>
     * String -> LocalDate
     * String -> LocalDateTime
     *
     * @return
     */
    @Bean
    public GenericConverter localDateTimeConverter() {
        return new GenericConverter() {
            @Override
            public Set<ConvertiblePair> getConvertibleTypes() {
                Set<ConvertiblePair> pairSet = new HashSet<>();
                pairSet.add(new ConvertiblePair(String.class, LocalDate.class));
                pairSet.add(new ConvertiblePair(String.class, LocalDateTime.class));
                return pairSet;
            }

            @Override
            public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                if (source == null || source.toString().trim().isEmpty()) return null;

                String pattern = null;
                if (targetType.hasAnnotation(DateTimeFormat.class)) {
                    DateTimeFormat dateTimeFormat = targetType.getAnnotation(DateTimeFormat.class);
                    if (dateTimeFormat.pattern() != null && !dateTimeFormat.pattern().isEmpty()) pattern = dateTimeFormat.pattern();
                }

                if (targetType.getType() == LocalDate.class) {
                    if (pattern == null || pattern.isEmpty()) pattern = dataProperties().getDateFormat();
                    return LocalDate.parse(source.toString(), DateTimeFormatter.ofPattern(pattern));
                }

                if (targetType.getType() == LocalDateTime.class) {
                    if (pattern == null || pattern.isEmpty()) pattern = dataProperties().getDateTimeFormat();
                    return LocalDateTime.parse(source.toString(), DateTimeFormatter.ofPattern(pattern));
                }

                return null;
            }
        };
    }

    /**
     * 自定义数字转换
     * <p>
     * String -> int/Integer
     * String -> float/Float
     * String -> double/Double
     *
     * @return
     */
    @Bean
    public GenericConverter numberConverter() {
        return new GenericConverter() {
            @Override
            public Set<ConvertiblePair> getConvertibleTypes() {
                Set<ConvertiblePair> pairSet = new HashSet<>();
                pairSet.add(new ConvertiblePair(String.class, int.class));
                pairSet.add(new ConvertiblePair(String.class, Integer.class));
                pairSet.add(new ConvertiblePair(String.class, float.class));
                pairSet.add(new ConvertiblePair(String.class, Float.class));
                pairSet.add(new ConvertiblePair(String.class, double.class));
                pairSet.add(new ConvertiblePair(String.class, Double.class));
                return pairSet;
            }

            @Override
            public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                if (source == null || source.toString().trim().isEmpty()) {
                    if (targetType.getType() == int.class || targetType.getType() == float.class || targetType.getType() == double.class) return 0;
                    return null;
                }

                DecimalFormat format = null;
                if (targetType.hasAnnotation(NumberFormat.class)) {
                    String pattern;
                    NumberFormat numberFormat = targetType.getAnnotation(NumberFormat.class);
                    if (numberFormat.pattern() == null || numberFormat.pattern().isEmpty()) {
                        pattern = dataProperties().getNumberFormat();
                    } else {
                        pattern = numberFormat.pattern();
                    }
                    if (pattern != null || !pattern.isEmpty()) {
                        pattern = pattern.replace(",", "");
                        format = new DecimalFormat(pattern);
                    }
                }

                String result = source.toString().trim().replace(",", "");
                if (targetType.getType() == int.class || targetType.getType() == Integer.class) {
                    return Integer.valueOf(result);
                } else if (targetType.getType() == float.class || targetType.getType() == Float.class) {
                    Float value = Float.parseFloat(result);
                    if (format == null) return value;
                    else return Float.parseFloat(format.format(value));
                } else if (targetType.getType() == double.class || targetType.getType() == Double.class) {
                    Double value = Double.parseDouble(result);
                    if (format == null) return value;
                    else return Double.parseDouble(format.format(value));
                }

                return null;
            }
        };
    }

    /**
     * 数据格式化配置
     */
    @Bean
    public DataConverterConfiguration.DataProperties dataProperties() {
        return new DataProperties();
    }

    @Setter
    @Getter
    @ConfigurationProperties("horizon.format")
    private class DataProperties {
        private String dateFormat = "yyyy-MM-dd";
        private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
        private String numberFormat = "";

        DataProperties() {
            DateUtil.setDATE(dateFormat);
            DateUtil.setDateTime(dateTimeFormat);
        }

        public void setDateFormat(String dateFormat) {
            if (dateFormat != null && !dateFormat.isEmpty()) {
                this.dateFormat = dateFormat;
                DateUtil.setDATE(dateFormat);
            }
        }

        public void setDateTimeFormat(String dateTimeFormat) {
            if (dateTimeFormat != null && !dateTimeFormat.isEmpty()) {
                this.dateTimeFormat = dateTimeFormat;
                DateUtil.setDateTime(dateTimeFormat);
            }
        }
    }
}
